#!/usr/bin/env python
# encoding: utf-8


"""
@file: geizhiqiuzhi.py
@time: 2016/10/28 上午10:54
"""
import re
from functools import reduce
from mathsolver.functions.base import *
from sympy import expand_trig, sin, cos, tan, simplify, cot, fraction, cancel, symbols, Integer, solveset
from mathsolver.functions.sanjiao import sanjiao_utils as su
from mathsolver.functions.sanjiao import hanshu as hu
from mathsolver.functions.budengshi import common_opers as co
from mathsolver.functions.sanjiao import sanjiaoxing_utils as san_utils
import copy
from mathsolver.functions.sanjiao.trig_condition_converts import condition_converts
from mathsolver.functions.sanjiao import Jiao
from mathsolver.functions.sanjiao.sanjiao_constants import QUADRANT_SEARCH_SUFFIX, EQS_SEARCH_KEY


# 由tanx求sin2x,cos2x等
# 把tanx cosx sinx 都求出来
# 已知tanα=2,则\\frac{sinα+2cosα}{cosα-sinα}=().
# 688564 已知tan(\\frac{π}{4}+α)=-3,则sinα*cosα=()
class QiuZhi001(BaseFunction):
    def solve_trig_value(self, eq, target_f):
        fl, fr = eq.sympify()
        t1, t1_value = su.std_simp_trig_eq(fl - fr).sympify()
        t1 = su.expand_tan_cot(t1)
        sub_t = su.trig_subs(t1)
        tvs = list(solveset(sub_t - t1_value, domain=S.Reals))
        d = hu.trig_der('T', tvs[0])
        e_f = expand_trig(target_f)
        e_f_symbols = list(e_f.free_symbols)
        if len(e_f_symbols) > 1:
            raise Exception('Type Match Error')
        sub_values = list(map(lambda t: (t[0], t[1][0] if co.iterable(t[1]) else t[1]), d.items()))
        e_f_v = simplify(su.trig_subs(e_f).subs(sub_values))
        self.steps.append(['%s = %s' % (new_latex(target_f), new_latex(e_f)), ''])
        self.steps.append(['  = ', new_latex(e_f_v)])
        self.output.append(BaseNumber(e_f_v))

    def solver(self, *args):
        self.label.add('解三角方程')
        if isinstance(args[0], BaseSinEq):
            fl, fr = args[0].sympify()
        else:
            fl, fr = args[0].sympify()
        if isinstance(args[1], BaseSinPoly):
            self.solve_trig_value(BaseEq([fl, fr]), args[1].sympify())
        elif isinstance(args[1], BaseSinPolys) or (isinstance(args[1], BaseMultiple) and args[1].type == 'SinPolys'):
            fs = args[1].sympify()
            for f in fs:
                self.solve_trig_value(BaseEq([fl, fr]), f)
        else:
            raise Exception('Can not solve the poly')
        return self


# 已知sinx+/-cosx=m,求sin2x [BUG]
# 联立方程组求解
class QiuZhi002(BaseFunction):
    def solver(self, *args):
        self.label.add('解三角方程')
        fl, fr = args[0].sympify()
        target_f = args[1].sympify()
        tl, tr = su.std_simp_trig_eq(fl - fr).sympify()
        tl = expand_trig(tl)
        self.steps.append([r'\because ', '%s = %s ' % (new_latex(fl), new_latex(tl))])
        self.steps.append([r'  ', '%s + %s =1 ' % (new_latex(sin('x') ** 2), new_latex(cos('x') ** 2))])
        tl = su.trig_subs(tl)
        a_v = list(solveset(tl - tr, symbol=sympify('A')))[0]
        eq = sympify('A**2 + C**2 - 1').subs('A', a_v)
        c_vs = list(solveset(eq, domain=S.Reals))
        a_vs = map(lambda c: a_v.subs('C', c), c_vs)
        e_f = expand_trig(target_f)
        ac_vs = zip(a_vs, c_vs)
        sub_acs = map(lambda ac: (('A', ac[0]), ('C', ac[1])), ac_vs)
        sub_acts = map(lambda ac: (ac[0], ac[1], ('T', ac[0][1] / ac[1][1]), ('B', ac[1][1] / ac[0][1])), sub_acs)
        self.steps.append([r'\therefore ', ' %s = %s' % (new_latex(target_f), new_latex(e_f))])
        e_f = su.trig_subs(e_f)
        e_f_vs = map(lambda ac: simplify(e_f.subs(ac)), sub_acts)
        e_f_vs = list(set(e_f_vs))[0]
        self.steps.append(['求得' + new_latex(target_f), '=' + new_latex(e_f_vs)])
        self.output.append(BaseNumber(e_f_vs))
        return self


# 已知2sin^{2}x-cos^{2}x+sinxcosx-6sinx+3cosx=0,求\\frac{2cosx(sinx+cosx)}{1+tanx}的值.
class QiuZhi004(BaseFunction):
    @staticmethod
    def std_f(f):
        mo, de = fraction(cancel(f))
        return mo

    def solver(self, *args):
        self.label.add('三角恒等变换求值')
        eq_l, eq_r = args[0].sympify()
        f1 = expand_trig(eq_l - eq_r)
        x_symbol = list(f1.free_symbols)[0]
        poly_f = expand_trig(args[1].sympify())
        trig_subs = ((sin(x_symbol), 'w'), (cos(x_symbol), 'x'), (tan(x_symbol), 'y'), (cot(x_symbol), 'z'))
        f1_sub = QiuZhi004.std_f(f1.subs(trig_subs))
        eqs = BaseEqs([[f1_sub, 0], ['w**2 + x**2', '1'], ['y*x', 'w'], ['z*w', 'x']])
        roots = co.solve_poly_eqs(eqs)
        poly_f_sub = poly_f.subs(trig_subs)
        poly_f_values = []
        for root in roots:
            v = simplify(poly_f_sub.subs(root))
            poly_f_values.append(v)
        poly_f_values = filter(lambda f_v: f_v.is_real, poly_f_values)
        v = list(set(poly_f_values))[0]
        self.steps.append(['求得%s=%s' % (new_latex(f1), new_latex(v)), ''])
        self.output.append(BaseNumber(v))
        return self


# 在△ABC中,cosA=\\frac{4}{5},tanB=2,则tan(2A+2B)的值为()
class QiuZhi005(BaseFunction):
    def solver(self, *args):
        self.label.add('三角恒等变换求值')
        eq_list = []
        if isinstance(args[0], BaseEq) or isinstance(args[0], BaseSinEq):
            _eq = args[0].sympify()
            eq_list.append(_eq)
        elif isinstance(args[0], BaseEqs) or isinstance(args[0], BaseSineqs):
            _eq_list = args[0].sympify()
            eq_list.extend(_eq_list)
        search_eqs = self.search(EQS_SEARCH_KEY)
        if search_eqs:
            eq_list.extend(search_eqs)
        symbs = co.eqs_symbs(eq_list)
        subs1 = []
        for index, symb in enumerate(symbs):
            sin_expr = sympify('sn' + str(symb) + str(index))
            cos_expr = sympify('cs' + str(symb) + str(index))
            tan_expr = sympify('tn' + str(symb) + str(index))
            cot_expr = sympify('ct' + str(symb) + str(index))
            sin_sub = (sin(symb), sin_expr)
            cos_sub = (cos(symb), cos_expr)
            tan_sub = (tan(symb), tan_expr)
            cot_sub = (cot(symb), cot_expr)
            subs1.append(sin_sub)
            subs1.append(cos_sub)
            subs1.append(tan_sub)
            subs1.append(cot_sub)
            eq_list.append([sin_expr ** 2 + cos_expr ** 2, 1])
        eq_list2 = []
        for _eq in eq_list:
            l, r = map(sympify, _eq)
            _f = expand_trig(l - r).subs(subs1)
            eq_list2.append([_f, '0'])
        solve_sets = co.isolve_eqs(BaseEqs(eq_list2))
        set_subs = [(v, k) for k, v in subs1]
        last_sets = []
        for _set in solve_sets:
            _result = map(lambda _: (_[0].subs(set_subs), _[1]), _set)
            last_sets.append(_result)
        # 过滤掉不符合条件的结果
        for symb in symbs:
            quad_key = sympify(str(symb) + QUADRANT_SEARCH_SUFFIX)  # 象限搜寻
            if self.search(quad_key):  # 如果有设置变量的象限
                quad = co.search_value(self, quad_key)
                quad = quad if not co.iterable(quad) else quad[0]
                last_sets = su.filter_quad_trig_values(last_sets, symb, quad)
            elif self.search(symb):  # 区间搜索
                symb_interval = self.search(symb)
                last_sets = su.filter_interval_trig_values(last_sets, symb, symb_interval)
        trig_values_print = ''
        f1 = args[1].sympify()
        f2 = expand_trig(f1)
        fs = []
        result_print = new_latex(f1) + '= ' + new_latex(f2) + ' = '
        _tmp_sets = []
        for _sets in last_sets:
            _values = [_v for _v in _sets]
            _m = dict(_sets)
            for _s in symbs:
                tan_value = _m.get(tan(_s), None)
                if not tan_value:
                    _tan_value = _m[sin(_s)] / _m[cos(_s)]
                    _values.append((tan(_s), _tan_value))
                cot_value = _m.get(cot(_s), None)
                if not cot_value:
                    _cot_value = _m[cos(_s)] / _m[sin(_s)]
                    _values.append((cot(_s), _cot_value))
            _tmp_sets.append(_values)
        last_sets = _tmp_sets
        # ---输出-------
        for _index, _tvs in enumerate(last_sets):
            vs_d = dict(_tvs)
            if _index == 0:
                _eqs = map(lambda _: (vs_d[sin(_)], vs_d[cos(_)]), symbs)
                trig_values_print += BaseEqs(_eqs).printing()
                _fv = f2.subs(_tvs)
                result_print += '' + new_latex(_fv)
                fs.append(_fv)
            else:
                _eqs = map(lambda _: (vs_d[sin(_)], vs_d[cos(_)]), symbs)
                trig_values_print += '或' + BaseEqs(_eqs).printing()
                _fv = f2.subs(_tvs)
                result_print += '或' + new_latex(_fv)
                fs.append(_fv)
        self.steps.append(['由题意可知:', trig_values_print])
        self.steps.append(['故 ', result_print])
        self.output.append(BaseSymbolValue({f1: fs}))
        return self


def condition_deep_deduce(condition, quad):
    """
    三角函数条件深度推导
    """

    def _condition_list_eqed(cond_list1, cond_list2):
        if len(cond_list1) != len(cond_list2):
            return False
        cond_f_list1 = map(lambda pair: sympify(pair[0]) - sympify(pair[1]), cond_list1)
        cond_f_list2 = map(lambda pair: sympify(pair[0]) - sympify(pair[1]), cond_list2)
        for cond_f in cond_f_list1:
            if cond_f not in cond_f_list2:
                return False
        return True

    def _haved_condition(cond_list, cond):
        cond_f_list = map(lambda pair: sympify(pair[0]) - sympify(pair[1]), cond_list)
        cond_f = sympify(cond[0]) - sympify(cond[1])
        for tmp_cond_f in cond_f_list:
            if tmp_cond_f == cond_f:
                return True
        return False

    prev_eqs = [list(condition.sympify()), ]
    next_eqs = [list(condition.sympify()), ]
    firsted = True
    while firsted or (not _condition_list_eqed(prev_eqs, next_eqs)):
        firsted = False
        prev_eqs = copy.copy(next_eqs)
        for eq_condition in prev_eqs:
            deduce_eqs = condition_converts(BaseEq(eq_condition), quad)
            for deduce_eq in deduce_eqs:
                if not _haved_condition(next_eqs, deduce_eq):
                    next_eqs.append(deduce_eq)
    return prev_eqs


# 已知一个值，列方程求解
class QiuZhi006(BaseFunction):
    @staticmethod
    def question_convert(expr):
        expr = sympify(expr)
        symb = list(expr.free_symbols)[0]
        half_tan_sub = (1 + tan(symb / 2)) / (1 - tan(symb / 2))
        half_sub_expr = expr.subs(half_tan_sub, 1)
        div_value = sympify(str(half_sub_expr / expr))
        # (1-tan(x/2))/(1+tan(x/2))
        if div_value != 1:
            return (cos(symb / 2) + sin(symb / 2)) / (cos(symb / 2) - sin(symb / 2)), (1 + sin(symb)) / cos(symb)
        return expr

    @staticmethod
    def q_cov_with_c(q, cond_trig_arg):
        """
        根据条件来转变式子。 比如 知道 cos(x/2)的值 求sin(x)的值
        :param q: 
        :param cond_trig_arg: 已知条件的三角函数参数
        :return: 
        """
        cond_trig_arg = sympify(cond_trig_arg)
        f = sympify(q)
        trig_mons = su.expr_trig_mons(q)
        q_args = map(lambda _: su.simp_trig_info(_)[1:3], trig_mons)
        for t_type, t_arg in q_args:
            if t_arg / cond_trig_arg == 2:  # 使用二倍角公式
                if t_type == sin:
                    t_sub = 2 * sin(cond_trig_arg) * cos(cond_trig_arg)
                    f = f.subs(t_type(t_arg), t_sub)
                elif t_type == cos:
                    t_sub = 2 * cos(cond_trig_arg) ** 2 - 1
                    f = f.subs(t_type(t_arg), t_sub)
        return f

    @staticmethod
    def _filter_trig_values(values_list, quad_or_intl):
        """
        过滤满足条件的值
        filter_trig_values([[[sin(x), 3/5], [cos(x), 4/5]], [[sin(x), 4/5], [cos(x), 3/5]]], Interval(pi, 3*pi/2))
        :param values_list: 三角函数值的列表
        :param quad_or_intl: 象限或者区间
        :return: 
        """
        verify_values_list = []
        for _values in values_list:
            trig_arg = _values[0][0].args[0]
            v_map = dict(_values)
            sin_v = v_map[sin(trig_arg)]
            cos_v = v_map[cos(trig_arg)]
            if isinstance(quad_or_intl, Interval):  # 区间
                _intl = su.angle_expr_interval(trig_arg, quad_or_intl)
                if su.tirg_value_in_interval(sin(trig_arg), sin_v, _intl) \
                        and su.tirg_value_in_interval(cos(trig_arg), cos_v, _intl):
                    verify_values_list.append(verify_values_list)
            elif isinstance(quad_or_intl, int) or isinstance(quad_or_intl, Integer):  # 象限值
                _intl = Jiao.quad_interval(quad_or_intl)
                _intl = su.angle_expr_interval(trig_arg, _intl)
                if su.tirg_value_in_interval(sin(trig_arg), sin_v, _intl) \
                        and su.tirg_value_in_interval(cos(trig_arg), cos_v, _intl):
                    verify_values_list.append(verify_values_list)
        return verify_values_list

    @staticmethod
    def _extend_trig_values(trig_expr, sin_value, cos_value):
        """
        根据sin(f)和cos(f)的值。扩展三角函数可以求出的值，比如知道 sin(f)的值 求出 f/2的三角函数值 2*f的三角函数值
        :param trig_expr: 
        :param sin_value: 
        :param cos_value
        :return: 
        """
        _values = []
        sin_arg = sympify(trig_expr).args[0]
        x1, y1 = symbols('x1, y1')  # x1 代表 sin(f/2); y1代表cos(f/2)
        eqs1 = [[2 * x1 * y1, sin_value], [x1 ** 2 + y ** 2, 1]]
        half_trig_values = co.isolve_eqs(BaseEqs(eqs1), [x1, y1])
        x2_value = 2 * sin_value * cos_value  # sin(2*f)
        y2_value = 2 * cos_value ** 2 - 1  # cos(2*f)
        double_trig_values = [[sin(2 * sin_arg), x2_value], [cos(2 * sin_arg), y2_value],
                              [tan(2 * sin_arg), x2_value / y2_value], [cot(2 * sin_arg), y2_value / x2_value]]
        return half_trig_values, double_trig_values

    def solver(self, *args):
        self.label.add('三角恒等变换求值')
        eq_l, eq_r = args[0].sympify()  # 等式
        eq_f = eq_l - eq_r
        du_p = r'du\(\d+?\)'
        _m = re.findall(du_p, str(eq_f))
        if _m:
            for tt in _m:
                new_tt = sympify(tt[3:-1]) * pi / 180
                eq_f = eq_f.subs({sympify(tt): new_tt})
        expand_eq_f = expand_trig(eq_f)
        f_symbs = list(expand_eq_f.free_symbols)
        if len(f_symbs) > 1:
            raise Exception('Type match error')
        self.steps.append(['\\because ' + args[0].printing(), ''])
        trig_values_list = su.trig_func_values(BaseEq([eq_f, '0']))
        values_print = ''
        for index, values in enumerate(trig_values_list):
            if index == 0:
                values_print += reduce(lambda a, b: a + ', ' + BaseEq(b).printing(), values, '')[1:]
            else:
                values_print += '或'
                values_print += reduce(lambda a, b: a + ', ' + BaseEq(b).printing(), values, '')[1:]
        self.steps.append(['所以求得 ' + values_print, ''])
        src_target_f = args[1].sympify()
        f = expand_trig(src_target_f)
        # sub_f = QiuZhi006.question_convert(f)
        sub_f_steps = QiuZhi006.question_convert(f)
        if sub_f_steps != f:
            sub_f = sub_f_steps[-1]
        else:
            sub_f = f
        symbs = list(f.free_symbols)
        if len(symbs) != 1:
            raise Exception('Type Match Error')
        symb = symbs[0]
        new_values_list = []
        quad_key = sympify(str(symb) + QUADRANT_SEARCH_SUFFIX)
        if self.search(quad_key):
            target_values = []
            quad = co.search_value(self, quad_key)
            quad = quad if not co.iterable(quad) else quad[0]
            for values in trig_values_list:
                trig_values_map = dict(values)
                sin_v = trig_values_map[sin(symb)]
                sin_pm = Jiao.trig_f_quad_pm('sin')[quad - 1]
                cos_v = trig_values_map[cos(symb)]
                cos_pm = Jiao.trig_f_quad_pm('cos')[quad - 1]
                if sin_v * sin_pm > 0 and cos_v * cos_pm > 0:
                    new_values_list.append(values)
            self.steps.append(['又\\because ' + new_latex(symb) + '在第' + str(quad) + '象限', ''])
            new_values_print = ''
            for values in new_values_list:
                values_print += reduce(lambda a, b: a + ', ' + BaseEq(b).printing(), values, '')[1:]
                values_print += '或'
                tmp_v = simplify(sub_f.subs(values))
                target_values.append(tmp_v)
            self.steps.append(['\\therefore ', new_values_print])
            if sub_f_steps != f:
                steps_print = ''
                for sub_step in sub_f_steps:
                    steps_print += '=' + new_latex(sub_step)
                self.steps.append(['\\because ' + new_latex(src_target_f), '=' + steps_print])
            else:
                self.steps.append(['\\because ' + new_latex(src_target_f), '=' + new_latex(sub_f)])
            trig_values_list = new_values_list
        elif self.search(symb):  # 如果有设置 变量的区间 比如 设α∈(0,\\frac{π }{2})
            target_values = []
            symb_interval = self.search(symb)
            self.steps.append(['又\\because ' + new_latex(symb) + ' \\in ' + new_latex(symb_interval), ''])
            for values in trig_values_list:
                trig_values_map = dict(values)
                sin_v = trig_values_map[sin(symb)]
                cos_v = trig_values_map[cos(symb)]
                if su.tirg_value_in_interval(sin(symb), sin_v, symb_interval) \
                        and su.tirg_value_in_interval(cos(symb), cos_v, symb_interval):
                    new_values_list.append(values)
            new_values_print = ''
            for index, values in enumerate(new_values_list):
                if index == 0:
                    new_values_print += reduce(lambda a, b: a + ', ' + BaseEq(b).printing(), values, '')[1:]
                else:
                    new_values_print += '或'
                    new_values_print += reduce(lambda a, b: a + ', ' + BaseEq(b).printing(), values, '')[1:]
                tmp_v = simplify(sub_f.subs(values))
                target_values.append(tmp_v)
            self.steps.append(['\\therefore ', new_values_print])
            if sub_f_steps != f:
                steps_print = ''
                for sub_step in sub_f_steps:
                    steps_print += '=' + new_latex(sub_step)
                self.steps.append(['\\because ' + new_latex(src_target_f), '=' + steps_print])
            else:
                self.steps.append(['\\because ' + new_latex(src_target_f), '=' + new_latex(sub_f)])
            trig_values_list = new_values_list
        else:
            target_values = []
            for values in trig_values_list:
                values_print += reduce(lambda a, b: a + ', ' + BaseEq(b).printing(), values, '')[1:]
                values_print += '或'
                tmp_v = simplify(sub_f.subs(values))
                target_values.append(tmp_v)
            if sub_f_steps != f:
                steps_print = ''
                for sub_step in sub_f_steps:
                    steps_print += '=' + new_latex(sub_step)
                self.steps.append(['\\because ' + new_latex(src_target_f), '=' + steps_print])
            else:
                self.steps.append(['\\because ' + new_latex(src_target_f), '=' + new_latex(sub_f)])
        target_values = list(set(target_values))
        if len(target_values) == 1 and not target_values[0].is_real:  # 如果结果表达式没有做任何变动, 那么说明没有做任何带入求值
            # 需要再次变动
            f = target_values[0]
            sub_f = QiuZhi006.q_cov_with_c(f, trig_values_list[0][0][0].args[0])
            self.steps.append(['所以' + new_latex(f), '=' + new_latex(sub_f)])
            target_values = []
            # ------------------------------------------------
            all_trig_values_list = []
            for trig_values in trig_values_list:
                t_arg = trig_values[0][0].args[0]
                v_m = dict(trig_values)
                sin_v = v_m[sin(t_arg)]
                cos_v = v_m[cos(t_arg)]
                tmp_values = []
                tmp_values.extend(trig_values)
                half_valus_list, double_values = su.half_and_double_trig_values(t_arg, sin_v, cos_v)
                tmp_values.extend(double_values)
                for half_values in half_valus_list:
                    tmp_values.extend(half_values)
                    all_trig_values_list.append(tmp_values)
            trig_values_list = all_trig_values_list
            # ------------------------------------------------
            for values in trig_values_list:
                values_print += reduce(lambda a, b: a + ', ' + BaseEq(b).printing(), values, '')[1:]
                values_print += '或'
                tmp_v = simplify(sub_f.subs(values))
                target_values.append(tmp_v)
        target_values = list(set(target_values))
        if len(target_values) == 1:
            self.steps.append(['故答案为:', '' + new_latex(target_values[0])])
        else:
            self.steps.append(['故答案为:', reduce(lambda _1, _2: _1 + '或' + new_latex(_2), target_values, '')])
            self.output.append(BaseSymbolValue({f: target_values}))
        return self


# 给值求值
class QiuZhi(BaseFunction):
    CLS = [QiuZhi006, QiuZhi001, QiuZhi002, QiuZhi004, QiuZhi005]

    def solver(self, *args):
        known_eqs = san_utils.known_triangle_eqs(self.known)
        if known_eqs:
            tmp_args = [known_eqs, ]
        else:
            tmp_args = []
        tmp_args.extend([arg for arg in args])
        solve_r = None
        for cl in QiuZhi.CLS:
            try:
                solve_r = cl(verbose=True)
                solve_r.known = self.known
                solve_r = solve_r.solver(*tmp_args)
                solve_r.label.add('三角恒等变换求值')
                break
            except Exception:
                solve_r = None
        if not solve_r:
            raise Exception('Can not solve the expression value')
        return solve_r


if __name__ == '__main__':
    pass
